package io.gitee.kotle.player.core.impl.exoplayer

import android.content.Context
import android.view.TextureView
import androidx.media3.common.C
import androidx.media3.common.C.WAKE_MODE_NETWORK
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.VideoSize
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.datasource.cache.CacheDataSource
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import io.gitee.kotle.player.core.IPlayer
import io.gitee.kotle.player.core.impl.BasePlayer
import io.gitee.kotle.player.download.impl.exoplayer.ExoPlayerDownloadImpl
import io.gitee.kotle.player.ui.ignoreError
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.math.max


/**
 *
 * 描述：使用exoplayer实现播放器的功能
 * [_downloader]下载器，同一个文件夹只能使用一个
 *
 * @author Jiale.Wei
 *
 * 创建时间：2023/4/12 16:11
 *
 */
open class ExoPlayerImpl<Model : IExoPlayerMode>(val context: Context, _realPlayer: ExoPlayer) :
    BasePlayer<MediaItem, Model>(), Player.Listener {
    /**
     * 真正的播放器
     */
    val realPlayer by lazy {
        _realPlayer.setForegroundMode(true)
        _realPlayer.setWakeMode(WAKE_MODE_NETWORK)
        _realPlayer.addListener(this)
        _realPlayer.repeatMode
        _realPlayer
    }

    override var volume: Float
        get() = realPlayer.volume
        set(value) {
            realPlayer.volume = value
        }

    override var speed: Float
        get() = realPlayer.playbackParameters.speed
        set(value) {
            realPlayer.setPlaybackSpeed(value)
            doListener { player, listener ->
                listener.onSpeedChange(player, realPlayer.playbackParameters.speed)
            }
        }

    override val duration: Long
        get() = max(0L, realPlayer.duration)

    override var currentPosition: Long
        get() = max(0L, realPlayer.currentPosition)
        set(value) {
            realPlayer.seekTo(value)
        }

    override val bufferedPosition: Long
        get() = max(0L, realPlayer.bufferedPosition)


    override var videoScaleType: IPlayer.VideoScaleType
        get() {
            return when (realPlayer.videoScalingMode) {
                C.VIDEO_SCALING_MODE_SCALE_TO_FIT -> IPlayer.VideoScaleType.FitXY
                C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING -> IPlayer.VideoScaleType.CenterCrop
                C.VIDEO_SCALING_MODE_DEFAULT -> IPlayer.VideoScaleType.Center
                else -> IPlayer.VideoScaleType.Center
            }
        }
        set(value) {
            realPlayer.videoScalingMode = when (value) {
                IPlayer.VideoScaleType.Center -> C.VIDEO_SCALING_MODE_DEFAULT
                IPlayer.VideoScaleType.CenterCrop -> C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
                IPlayer.VideoScaleType.FitXY -> C.VIDEO_SCALING_MODE_SCALE_TO_FIT
            }
            doListener { player, listener ->
                listener.onVideoScaleTypeChange(player, value)
            }
        }

    override val videoSize: Triple<Int, Int, Int>
        get() = Triple(
            realPlayer.videoSize.width,
            realPlayer.videoSize.height,
            realPlayer.videoSize.unappliedRotationDegrees,
        )

    override fun play() {
        realPlayer.play()
    }

    override fun pause() {
        realPlayer.pause()
    }

    override fun stop() {
        super.stop()
        when (currentModelState) {
            IPlayer.ModelState.End,
            IPlayer.ModelState.Stop -> {
                //不做处理
            }

            IPlayer.ModelState.Buffer,
            IPlayer.ModelState.Error,
            IPlayer.ModelState.Pause,
            IPlayer.ModelState.Play,
            IPlayer.ModelState.Prepare -> {
                updateModelState(IPlayer.ModelState.Stop)
            }
        }
        realPlayer.stop()
    }

    override fun attachTextureView(view: TextureView?) {
        realPlayer.setVideoTextureView(view)
    }

    override suspend fun onStartPrepare(
        scope: CoroutineScope,
        player: IPlayer<MediaItem, Model>,
        model: Model
    ) {
        val mediaId = model.getMediaId(scope, player)
        val realDownloader = downloader
        val download = if (realDownloader is ExoPlayerDownloadImpl) {
            withContext(Dispatchers.IO) {
                realDownloader.getDownload(mediaId)?.media
            }
        } else {
            null
        }
        if (download != null && realDownloader is ExoPlayerDownloadImpl) {//播放下载
            val startPositionMs = model.getPlayPosition(scope, player, mediaId, download)
            val cacheDataSourceFactory = CacheDataSource.Factory()
                .setCache(realDownloader.onCreateCache())
                .setUpstreamDataSourceFactory(DefaultHttpDataSource.Factory())
                .setCacheWriteDataSinkFactory(null)
            val mediaSource = DefaultMediaSourceFactory(cacheDataSourceFactory)
                .createMediaSource(download)
            realPlayer.setMediaSource(mediaSource, startPositionMs)
        } else {//播放网络
            val media = model.getMedia(scope, player, mediaId)
            val startPositionMs = model.getPlayPosition(scope, player, mediaId, media)
            realPlayer.setMediaItem(media, startPositionMs)
        }
        realPlayer.prepare()
    }

    override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
        super.onPlayWhenReadyChanged(playWhenReady, reason)
        checkStatus()
    }


    override fun onPlaybackStateChanged(@Player.State playbackState: Int) {
        super.onPlaybackStateChanged(playbackState)
        checkStatus()
    }

    private fun checkStatus() {
        ignoreError {
            when (realPlayer.playbackState) {
                Player.STATE_BUFFERING -> {
                    updateModelState(if (realPlayer.playWhenReady) IPlayer.ModelState.Buffer else IPlayer.ModelState.Pause)
                }

                Player.STATE_ENDED -> {
                    updateModelState(IPlayer.ModelState.End)
                }

                Player.STATE_IDLE -> {

                }

                Player.STATE_READY -> {
                    updateModelState(if (realPlayer.playWhenReady) IPlayer.ModelState.Play else IPlayer.ModelState.Pause)
                }

                else -> {}
            }
        }
    }

    override fun onPlayerError(error: PlaybackException) {
        super.onPlayerError(error)
        updateModelState(IPlayer.ModelState.Error)
        doListener { player, listener ->
            listener.onError(player, error)
        }
    }

    override fun onVideoSizeChanged(videoSize: VideoSize) {
        super.onVideoSizeChanged(videoSize)
        doListener { player, listener ->
            listener.onVideoSizeChange(
                player,
                videoSize.width,
                videoSize.height,
                videoSize.unappliedRotationDegrees,
                videoSize.pixelWidthHeightRatio,
            )
        }
    }

    override fun onRenderedFirstFrame() {
        super.onRenderedFirstFrame()
        doListener { player, listener ->
            listener.onRenderedFirstFrame(player)
        }
    }

    override suspend fun onRelease(scope: CoroutineScope) {
        super.onRelease(scope)
        realPlayer.release()
    }
}